package com.sean.reflect.anno;

import static java.lang.annotation.ElementType.*;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;

public class Foo {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, Exception {
        Class<Foo1> clz = Foo1.class;
        System.out.println("clz.getAnnotatedInterfaces()");
        printAnnotatedType(clz.getAnnotatedInterfaces());
        System.out.println("clz.getAnnotatedSuperclass()");
        printAnnotatedType(clz.getAnnotatedSuperclass());

        Method method = clz.getMethod("getFoo2", String.class);
        System.out.println("method.getAnnotatedReturnType()");
        printAnnotatedType(method.getAnnotatedReturnType());
        System.out.println("method.getAnnotatedReceiverType()");
        printAnnotatedType(method.getAnnotatedReceiverType());
        System.out.println("method.getAnnotatedParameterTypes()");
        printAnnotatedType(method.getAnnotatedParameterTypes());
        System.out.println("method.getAnnotatedExceptionTypes()");
        printAnnotatedType(method.getAnnotatedExceptionTypes());

        Constructor<Foo1> constructor = clz.getConstructor(String.class);
        System.out.println("constructor.getAnnotatedReturnType()");
        printAnnotatedType(constructor.getAnnotatedReturnType());
        System.out.println("constructor.getAnnotatedReceiverType()");
        printAnnotatedType(constructor.getAnnotatedReceiverType());
        System.out.println("constructor.getAnnotatedParameterTypes()");
        printAnnotatedType(constructor.getAnnotatedParameterTypes());
        System.out.println("constructor.getAnnotatedExceptionTypes()");
        printAnnotatedType(constructor.getAnnotatedExceptionTypes());

        Field field = clz.getField("foo2");
        System.out.println("field.getAnnotatedType()");
        printAnnotatedType(field.getAnnotatedType());

        Parameter[] parameter = method.getParameters();
        System.out.println("parameter[0].getAnnotatedType()");
        printAnnotatedType(parameter[0].getAnnotatedType());

    }

    private static void printAnnotatedType(AnnotatedType[] annotatedTypes) {
        for (AnnotatedType annotatedType : annotatedTypes) {
            System.out.println(Arrays.toString(annotatedType.getAnnotations()));
            System.out.println(annotatedType.getType());
        }

    }

    private static void printAnnotatedType(AnnotatedType annotatedType) {
        System.out.println(Arrays.toString(annotatedType.getAnnotations()));
        System.out.println(annotatedType.getType());
    }
}

@Custom
class Foo1 extends @Custom Employee implements @Custom Bar {
    public @Custom String foo2;

    public @Custom Foo1(@Custom String foo2) throws @Custom Exception {
        this.foo2 = foo2;
    }

    public @Custom String getFoo2(@Custom Foo1 this, @Custom String foo3) throws @Custom Exception {
        return foo3;
    }
}

class Employee {

}

interface Bar {

}

// So that annotation metadata is available at run time
@Retention(RetentionPolicy.RUNTIME)
// TYPE_USE being the important one
@Target(value = {ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE, TYPE_PARAMETER,
        TYPE_USE})
@interface Custom {
}


